/* ascii_gyro.c contains all the gyro code, written to interpret messages
   from the gyroscope used in the D4 group project. Two functions are
   currently implemented - a mono synthesiser where the current note is
   controlled by the gyroscope position, and a sequencer which maps sequences
   to gyroscope positions. 
   
   Copyright (C) 2014 James Boyle

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
#include "rpi/ascii_gyro.h"
#include "asciiboard.h"
#include "asciiboard_const.h"

static void init_sequencer(SEQUENCER *const seq);
static void sequence_tick(SEQUENCER *const seq);
static void parse(SEQUENCER *const seq, const int gyro_pos);
static void record_code(SEQUENCER *const seq, const char ch,
                        const int gyro_pos);
static void add_to_sequence(SEQUENCER *const seq, const char ch);

int gyro_synth(const char ch)
{
	static int mode = MONO_MODE;

	/* if data is being streamed, ignore any input, as some characters used to
	   transmit data are the same as those used by the gyros */
	if (streaming_data())
		return NO_ERROR;

	if (ch == GYRO_MONO_CODE)
		mode = MONO_MODE;
	else if (ch == GYRO_SEQUENCER_CODE)
		mode = SEQUENCER_MODE;

	if (mode == MONO_MODE)
		gyro_mono_synth(ch);
	else
		gyro_sequencer(ch);

	return NO_ERROR;
}

/* gyro_mono_synth() allows a gyro to control a voice on the synth, where each
   gyro is assigned 12 tones (an octave).  The synth can only control a single
   voice. */
int gyro_mono_synth(const char ch)
{
	static int gyro_on[GYRO_COUNT] = {TRUE, TRUE};
	static int octave[GYRO_COUNT] = {0, 0};
	static int curr_tone[GYRO_COUNT] = {GYRO_FIRST_TONE, GYRO_FIRST_TONE};
	int i, t, gyro, mapping_start, new_octave;
	int first_tone, last_tone;
	const float *prev_map;
	float new_map[TONE_COUNT], scale = 1.0f;

	/* if data is being streamed, ignore any input, as some characters used to
	   transmit data are the same as those used by the gyros */
	if (streaming_data())
		return NO_ERROR;

	/* if the gyro is being reset */
	if (ch == RESET_CODE || ch == SILENCE_CODE)
	{
		gyro_on[GYRO_ONE] = gyro_on[GYRO_TWO] = TRUE;
		curr_tone[GYRO_ONE] = curr_tone[GYRO_TWO] = GYRO_FIRST_TONE;
		/* assume the tone map is reset by the user */
		octave[CH_ONE] = octave[CH_TWO] = 0;

		return NO_ERROR;
	}

	/* if the gyros are being toggled on or off */
	if (ch == GYRO_ONE_TOGGLE_CODE)
	{
		gyro_on[GYRO_ONE] = !gyro_on[GYRO_ONE];
		return NO_ERROR;
	}
	else if (ch == GYRO_TWO_TOGGLE_CODE)
	{
		gyro_on[GYRO_TWO] = !gyro_on[GYRO_TWO];
		return NO_ERROR;
	}

	if (ch >= GYRO_ONE_MAPPING_START && ch <= GYRO_ONE_MAPPING_END)
	{
		/* if the gyro is switched off, ignore input */
		if (!gyro_on[GYRO_ONE])
			return NO_ERROR;
		gyro = GYRO_ONE;
		mapping_start = GYRO_ONE_MAPPING_START;
		first_tone = 0;
		last_tone = OCTAVE_TONES-1;
	}
	else if (ch >= GYRO_TWO_MAPPING_START && ch <= GYRO_TWO_MAPPING_END)
	{
		/* if the gyro is switched off, ignore input */
		if (!gyro_on[GYRO_TWO])
			return NO_ERROR;
		gyro = GYRO_TWO;
		mapping_start = GYRO_TWO_MAPPING_START;
		first_tone = OCTAVE_TONES;
		last_tone = 2*OCTAVE_TONES - 1;
	}
	else
		return NO_ERROR; /* ignore any other characters */

	/* each gyro has a 12 tones (an octave) mapped to it, but the gyro can
	   shift any number of octaves up or down by rewriting the tone map */
	t = ch - mapping_start;
	/* calculate the octave of this tone */
	new_octave = (int) ((float) t / OCTAVE_TONES);
	/* if the octave has changed, update the octave and tone map */
	if (new_octave != octave[gyro])
	{
		prev_map = read_tone_map(CH_ONE);
		/* calculate how much to scale each tone (going up an octave
			is the same as doubling frequency */
		scale = 1.0f;
		while (new_octave > octave[gyro])
		{
			scale *= 2.0f;
			octave[gyro]++;
		}
		while (new_octave < octave[gyro])
		{
			scale *= 0.5f;
			octave[gyro]--;
		}

		/* rewrite the tone map with the 12 tones shift up / down the
		   correct number of octaves */
		for (i = 0; i < first_tone; i++)
			new_map[i] = prev_map[i];
		for (; i <= last_tone; i++)
			new_map[i] = prev_map[i] * scale;
		for (; i < TONE_COUNT; i++)
			new_map[i] = prev_map[i];
		set_tone_map(new_map, TONE_COUNT, CH_ONE);
	}

	/* work out which note it is in the 12 tone scale */
	t = t % OCTAVE_TONES;
	/* gyro 1 uses tones 0-11, gyro 2 uses tones 12-23 */
	t += OCTAVE_TONES * gyro;
	/* transmit the tone character using ascii_through_release(), meaning the note
	   will have a release envelope */
	t += 'a';
	/* if a tone is playing, stop the previous note and start the next note */
	if (curr_tone[gyro] != GYRO_FIRST_TONE)
		transmit_ch(curr_tone[gyro]);
	/* remember the last played tone */
	curr_tone[gyro] = t;
	transmit_ch(t);

	return NO_ERROR;
}

/* init_sequencer() sets the sequencer to an empty reset state. */
void init_sequencer(SEQUENCER *const seq)
{
	int i;

	seq->sequence_len = 0;
	for (i = 0; i < RECORD_MAX; i++)
		seq->recorded_len[i] = 0;
}

/* record_code() records the current character to the given gyro
   position in the sequencer. */
void record_code(SEQUENCER *const seq, const char ch,
                        const int gyro_pos)
{
	if (seq->recorded_pos < RECORD_MAX)
	{
		seq->recorded[gyro_pos][seq->recorded_pos] = ch;
		seq->recorded_pos++;
	}
}

/* add_to_sequence() adds a given code to the current sequence, if there's
   space. */
void add_to_sequence(SEQUENCER *const seq, const char code)
{
	if (seq->sequence_len < RECORD_MAX)
	{
		seq->sequence[seq->sequence_len] = code;
		seq->sequence_len++;
	}
}

/* parse() interprets the input sequence, and produces an output string.
   Every period may contain maximum one of each tone character, where
   tones are switched off after every period.  If a looping code
   is read, parsing is stopped and ended with the looping code. */
void parse(SEQUENCER *const seq, const int gyro_pos)
{
	int i, r, curr;
	int code_used[CODE_COUNT];

	for (i = 0; i < CODE_COUNT; i++)
		code_used[i] = FALSE;

	/* parse entire input array */
	for (r = 0; r < seq->recorded_len[gyro_pos] &&
	     seq->sequence_len < RECORD_MAX; r++)
	{
		curr = seq->recorded[gyro_pos][r];
		if (curr == GYRO_NEXT_SEQ_POS_CODE)
		{
			add_to_sequence(seq, NEXT_CODE);
			/* reset the array of used codes ie go to the next period */
			for (i = 0; i < CODE_COUNT; i++)
				code_used[i] = FALSE;
		}
		else if (curr == GYRO_LOOP_CODE)
		{
			add_to_sequence(seq, GYRO_LOOP_CODE);
			break;
		}
		else if (!code_used[curr])
		{
			code_used[curr] = TRUE;
			add_to_sequence(seq, curr);
		}
		/* otherwise ignore, as a code may only be used once a period */
	}
}

/* sequence_tick() loads the set of parsed characters for this period,
   transmits them to the synth, and then increments the position.  If
   the end of the sequence is reached then the sequencer stops.  If
   the looping code is encountered the sequencer loops back to the
   start (and continues). */
void sequence_tick(SEQUENCER *const seq)
{
	int pos;

	for (pos = seq->sequence_pos; pos < seq->sequence_len; pos++)
	{
		if (seq->sequence[pos] == NEXT_CODE)
		{
			pos++;
			break;
		}
		else if (seq->sequence[pos] == GYRO_LOOP_CODE)
		{
			/* loop back to the start of the sequence */
			pos = 0;
			break;
		}
		else /* transmit the character */
			transmit_ch(seq->sequence[pos]);
	}
	seq->sequence_pos = pos;
}

/* gyro_sequencer() is a fully working sequencer, which allows the gyroscope
   to map an arbitrary sequence of characters to given gyro positions.  When
   a gyro position is sent, the sequencer loads this position.  If a sequence
   is stored it is loaded with parse() and played with sequencer_tick().  If
   the user starts mapping, then the following input is mapped to the position.
   When the user stops mapping, this sequence is immediately loaded and starts
   playing.  A loop can be created by storing the loop code 'L' at the end of
   a sequence. */
int gyro_sequencer(const char ch)
{
	static SEQUENCER seq;
	static int gyro_pos = 0;
	static int init = FALSE, mapping = FALSE;
	/* if data is being streamed, ignore any input, as some characters used to
	   transmit data are the same as those used by the gyros */
	if (streaming_data())
		return NO_ERROR;

	/* if the gyro is being reset */
	if (ch == RESET_CODE)
		init = FALSE;

	if (init == FALSE)
	{
		init_sequencer(&seq);
		init = TRUE;
	}

	/* the next code is being transmitted too fast and would fill up the sequencer
	   instantly in a realtime system, so an explicit next sequence position code
	   is defined when mapping */
	if (ch == NEXT_CODE )
	{
		if(mapping)
			return NO_ERROR;
		else
			sequence_tick(&seq);
	}
	/* if starting or stopping a sequence map */
	else if (ch == GYRO_MAP_SEQUENCE_CODE)
	{
		/* if stopping a mapping */
		if (mapping)
		{
			seq.recorded_len[gyro_pos] = seq.recorded_pos;
			mapping = FALSE;
			/* parse the current sequence */
			parse(&seq, gyro_pos);
		}
		else
		{
			/* start mapping */
			mapping = TRUE;
		}
	}
	/* if not in the mapping mode and a (different) mapping code is sent,
	   set the sequencer to the new gyro position */
	else if (!mapping && ch >= GYRO_ONE_MAPPING_START && ch <= GYRO_TWO_MAPPING_END)
	{
		if ((int) ch != gyro_pos)
		{
			gyro_pos = (int) ch;
			/* stop any previously playing tones */
			transmit_ch(SILENCE_CODE);
			/* reset the sequence */
			seq.sequence_pos = seq.sequence_len = 0;
			/* parse the new sequence */
			parse(&seq, gyro_pos);
		}
	}
	/* if mapping, record any other characters to the currently selected
	   sequence */
	else if (mapping)
		record_code(&seq, ch, gyro_pos);

	return NO_ERROR;
}
